/* Changes:
 *
 * printSegment replaced with toString
 *   this is a cleaner, more flexible and correct way to print custom classes
 */

package gss;
import org.apache.commons.math.fraction.BigFraction;
import java.util.Vector;

/**
 *
 * @author mdm32
 */

public class LineSegment {
    private String lName;
    private BigFraction X1;
    private BigFraction Y1;
    private BigFraction X2;
    private BigFraction Y2;


    //constructors
    public LineSegment(){
        lName = "0";
        X1 = new BigFraction(0);
        Y1 = new BigFraction(0);
        X2 = new BigFraction(1);
        Y2 = new BigFraction(1);
    }

    public LineSegment(String name, BigFraction x1, BigFraction y1,
            BigFraction x2, BigFraction y2){
        lName = name;
        X1 = x1;
        Y1 = y1;
        X2 = x2;
        Y2 = y2;
    }

    //recover data
    public String getName(){
        return lName;
    }
    public BigFraction getX1(){
        return X1;
    }
    public BigFraction getX2(){
        return X2;
    }
    public BigFraction getY1(){
        return Y1;
    }
    public BigFraction getY2(){
        return Y2;
    }

    //set data
    public void setName(String s){
        lName = s;
    }
    public void setX1(BigFraction x1){
        X1 = x1;
    }
    public void setX2(BigFraction x2){
        X2 = x2;
    }
    public void setY1(BigFraction y1){
        Y1 = y1;
    }
    public void setY2(BigFraction y2){
        Y2 = y2;
    }

    //additional methods
    public double segmentLength(){
        BigFraction cy = Y2.subtract(Y1);
        BigFraction cx = X2.subtract(X1);
        return Math.sqrt(((cx.multiply(cx)).add(cy.multiply(cy))).doubleValue());
    }
    public String toString(){
        return (lName + " (" + X1 + "," + Y1 +
                "), (" + X2 + "," + Y2 + ")");
    }

    /*returns true if the segment is vertical
      */
    public boolean isNotVertical(){
        if(X1.equals(X2))
            return false;
        return true;
    }

    /*returns the y evaluation at x
      */
    public BigFraction yValue(BigFraction x){
        if(isNotVertical()){
            //(Y2-Y1)*x/(X2-X1)+Y1-(X1*(Y2-Y1)/(X2-X1))
            return (Y2.subtract(Y1)).multiply(x.divide(X2.subtract(X1))).add(Y1.subtract(X1.multiply(Y2.subtract(Y1)).divide(X2.subtract(X1))));
        }
        else{
            return new BigFraction(0);
      }
    }

      /*returns the slope if not infinite
       * otherwise it returns BigFraction(0) as if horizontal
       */
      public BigFraction slope(){
          if(isNotVertical()){
              return (Y2.subtract(Y1)).divide(X2.subtract(X1));
          }
          else{
            return new BigFraction(0);
          }
      }

      //overrides objects equals method
      @Override
      public boolean equals(Object b){
        boolean first = (b!=null) && (b.getClass() == this.getClass())
                && (this.getName().equals(((LineSegment)b).getName()));
        boolean seconda = this.getX1().equals(((LineSegment)b).getX1())
                && this.getY1().equals(((LineSegment)b).getY1())
                && this.getX2().equals(((LineSegment)b).getX2())
                && this.getY2().equals(((LineSegment)b).getY2());
        boolean secondb = this.getX1().equals(((LineSegment)b).getX2())
                && this.getY1().equals(((LineSegment)b).getY2())
                && this.getX2().equals(((LineSegment)b).getX1())
                && this.getY2().equals(((LineSegment)b).getY1());
        return first && (seconda || secondb);
      }

    /*Calculates the coordinates where segments a and b intersect
     * returns a vertex with this BigFraction coordinate.
     * This method assumes an intersection exists because it is only called
     * after this has been established with doIntersect
     */
       public Vertex intersection(LineSegment b){
        BigFraction x = new BigFraction(0);
        BigFraction y = new BigFraction(0);

        BigFraction x1 = X1;
        BigFraction y1 = Y1;
        BigFraction x2 = X2;
        BigFraction y2 = Y2;
        BigFraction x3 = b.getX1();
        BigFraction y3 = b.getY1();
        BigFraction x4 = b.getX2();
        BigFraction y4 = b.getY2();


        Vector<String> segs = new Vector<String>();
        segs.add(lName);
        segs.add(b.getName());


        if(x1.equals(x2)){
            x=x1;
            BigFraction s2 = (y4.subtract(y3)).divide(x4.subtract(x3));
            y=s2.multiply(x.subtract(x3)).add(y3);
        }
        else if(x3.equals(x4)){
            x=x3;
            BigFraction s1 = (y2.subtract(y1)).divide(x2.subtract(x1));
            y = s1.multiply(x.subtract(x1)).add(y1);
        }
        else{
            BigFraction s1 = (y2.subtract(y1)).divide(x2.subtract(x1));
            BigFraction s2 = (y4.subtract(y3)).divide(x4.subtract(x3));
            //x = (x1*(x4*(y3-y2)+x3*(y2-y4))+x2*(x4*(y1-y3)+x3*(y4-y1)))/(-(x3-x4)*(y1-y2)+(x1-x2)*(y3-y4));
            x = (x1.multiply(x4.multiply(y3.subtract(y2)).add(x3.multiply(y2.subtract(y4))) ).add(x2.multiply(x4.multiply(y1.subtract(y3) ).add(x3.multiply(y4.subtract(y1) )) ))).divide(((x4.subtract(x3)).multiply(y1.subtract(y2)).add((x1.subtract(x2) ).multiply(y3.subtract(y4)))));
            //y = s1*(x-x1)+y1;
            y = s1.multiply(x.subtract(x1)).add(y1);

        }

        return new Vertex(segs, x, y);
    }

    /*checks if segments a and b intersect
     * returns true if an intersection exists
     */
    public boolean doIntersect(LineSegment b){
        BigFraction x1 = X1;
        BigFraction y1 = Y1;
        BigFraction x2 = X2;
        BigFraction y2 = Y2;
        BigFraction x3 = b.getX1();
        BigFraction y3 = b.getY1();
        BigFraction x4 = b.getX2();
        BigFraction y4 = b.getY2();

        if(yValue(x3).equals(y3) && ((x1.compareTo(x3)<=0 && x2.compareTo(x3)>=0) ||
                ((x1.compareTo(x3)>=0 && x2.compareTo(x3)<=0)))){
            return true;
        }
        if(yValue(x4).equals(y4) && ((x1.compareTo(x4)<=0 && x2.compareTo(x4)>=0) ||
                ((x1.compareTo(x4)>=0 && x2.compareTo(x4)<=0)))){
            return true;
        }
        if(b.yValue(x1).equals(y1) && ((x3.compareTo(x1)<=0 && x4.compareTo(x1)>=0) ||
                ((x3.compareTo(x1)>=0 && x4.compareTo(x1)<=0)))){
            return true;
        }
        if(b.yValue(x2).equals(y2) && ((x3.compareTo(x2)<=0 && x4.compareTo(x2)>=0) ||
                ((x3.compareTo(x2)>=0 && x4.compareTo(x2)<=0)))){
            return true;
        }
        //double det123 = (x2 - x1)*(y3 - y1) - (x3 - x1)*(y2 - y1);
        BigFraction det123 = ((x2.subtract(x1)).multiply(y3.subtract(y1))).subtract((x3.subtract(x1)).multiply(y2.subtract(y1)));
        //double det124 = (x2 - x1)*(y4 - y1) - (x4 - x1)*(y2 - y1);
        BigFraction det124 = ((x2.subtract(x1)).multiply(y4.subtract(y1))).subtract((x4.subtract(x1)).multiply(y2.subtract(y1)));
        //double det341 = (x3 - x1)*(y4 - y1) - (x4 - x1)*(y3 - y1);
        BigFraction det341 = ((x3.subtract(x1)).multiply(y4.subtract(y1))).subtract((x4.subtract(x1)).multiply(y3.subtract(y1)));

        BigFraction det342 = det123.subtract(det124).add(det341);

        BigFraction zero = new BigFraction(0);

        if((det123.multiply(det124)).compareTo(zero)==-1 && (det341.multiply(det342)).compareTo(zero)==-1)
            return true;
        if((det123.multiply(det124)).equals(zero) && (det341.multiply(det342)).equals(zero))
            return true;

        return false;
    }
      /*tests if segments a and b are parallel
        * if so, return true
        */
    public boolean areParallel(LineSegment b){
        BigFraction x1 = X1;
        BigFraction y1 = Y1;
        BigFraction x2 = X2;
        BigFraction y2 = Y2;
        BigFraction x3 = b.getX1();
        BigFraction y3 = b.getY1();
        BigFraction x4 = b.getX2();
        BigFraction y4 = b.getY2();


        if(x1.equals(x2) || x3.equals(x4)){
            if(x1.equals(x2) && x3.equals(x4)){
                return true;
            }
            else{
                return false;
            }
        }
        else{
            BigFraction s1 = (y2.subtract(y1)).divide(x2.subtract(x1));
            BigFraction s2 = (y4.subtract(y3)).divide(x4.subtract(x3));
            if(s1.equals(s2))
                return true;
        }

        return false;
    }

}